package org.oep.widgets;

import java.io.IOException;
import java.util.Vector;

import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Image;

public class ChoiceWidget extends Widget {
	private Vector choices = new Vector();
	private int type = TYPE_LIST;
	private int diameter = 0;
	private int minimumDiameter = 0;
	private int direction = DIR_DOWN;
	private int choiceIndex = 0;
	private Font defaultFont = Font.getDefaultFont();
	private Font selectedFont = Font.getDefaultFont();
	private Image cursor;
	
	public static final int DIR_DOWN = 0;
	public static final int DIR_UP = 1;
	
	public static final int TYPE_CIRCULAR = 0;
	public static final int TYPE_LIST = 1;
	public static final int TYPE_BAR = 2;
	
	private static final int PADDING = 2;
	private static final int BG_COLOR = 255;
	
	private static final int ACTIVE_ITEM_COLOR = 0;
	private static final int INACTIVE_ITEM_COLOR = 225;
	
	private static final int STEPS = 5;
	
	private int shiftAmount = 0;
	private int pidgeonShift = 0;
	
	/**
	 * Create a new ChoiceMenu and specify the type.
	 * @param type, either TYPE_CIRCULAR, TYPE_LIST, TYPE_BAR
	 */
	public ChoiceWidget(int type) {
		this.type = type;
		try {
			cursor = Image.createImage("/res/graphics/cursor.png");
		}
		catch(IOException e) {
			System.err.println(e);
		}
		active = false;
	}
	
	/**
	 * Removes all the current choices so the menu can be repopulated.
	 */
	public void clear() {
		choices.removeAllElements();
		choiceIndex = 0;
		active = false;
	}
	
	public void setType(int type) {
		this.type = type;
	}
	
	public void setDirection(int direction) {
		this.direction = direction;
	}
	
	public int addChoice(Object b) {
		choices.addElement(b);
		
		String s = b.toString();
		int width = defaultFont.charsWidth(s.toCharArray(), 0, s.length()) / 2;
		int height = defaultFont.getHeight() / 2;
		
		minimumDiameter = Math.max(minimumDiameter, 2 * (int)Math.sqrt(width * width + height * height));
		diameter = Math.max(diameter, minimumDiameter);
		
		return choices.size() - 1;
	}
	
	public void paint(Graphics g, int x, int y) {
		if(!active || choices.size() <= 0) return;
		
		int c = g.getColor();
		Font f = g.getFont();
		g.setFont(defaultFont);
		if(type == TYPE_LIST) {
			paintList(g,x,y);
		}
		else if(type == TYPE_CIRCULAR)
			paintCircular(g,x,y);
		
		else if(type == TYPE_BAR) {
			paintBar(g,x,y);
		}
		g.setFont(f);
		g.setColor(c);
	}
	
	public void paintCircular(Graphics g, int x, int y) {
		if(choices.size() <= 0 || diameter <= 0) return;
		
		int d = diameter - 4;
		
		// Difference in diameter :)
		int dd = Math.abs(diameter - d);
		
		g.setColor(0);
		g.fillArc(x - diameter / 2, y - diameter / 2, diameter, diameter, 0, 360);
		g.setColor(0xFFFFFF);
		g.fillArc(x - diameter / 2 + dd / 2, y - diameter / 2 + dd / 2, d, d, 0, 360);
		
		int stepAngle = 360 / choices.size();
		g.setColor(0xCCCCCC);
		g.fillArc(x - diameter / 2 + dd / 2, y - diameter / 2 + dd / 2, d, d, -choiceIndex * stepAngle, stepAngle);
	
		g.setColor(0);
		String s = choices.elementAt(choiceIndex).toString();
		g.drawString(s, x, y + defaultFont.getHeight() / 2, Graphics.HCENTER | Graphics.BOTTOM);
	}
	
	public void paintList(Graphics g, int x, int y) {
		int mode;
		if(direction == DIR_UP) {
			mode = Graphics.BOTTOM | Graphics.HCENTER;
		} else {
			mode = Graphics.TOP | Graphics.HCENTER;
		}
		
		// Modifier for the y-coordinate
		int p = 0;
		
		// Calculate the widest choice
		int max = 0;
		for(int i = 0; i < choices.size(); i++) {
			String s = choices.elementAt(i).toString();
			max = Math.max(selectedFont.charsWidth(s.toCharArray(), 0, s.length()), max);
		}
		
		g.setColor(0);
		for(int i = 0; i < choices.size(); i++) {
			switch(direction) {
			case DIR_UP: p -= defaultFont.getHeight(); break;
			case DIR_DOWN: p += defaultFont.getHeight(); break;
			}
			
			String s = choices.elementAt(i).toString();
			
			if(i == choiceIndex) {
				g.setFont(selectedFont);
				g.drawImage(cursor, x - max / 2 - 5, y + p + selectedFont.getHeight() / 2, Graphics.RIGHT | Graphics.VCENTER);
			} else {
				g.setFont(defaultFont);
			}
			g.drawString(s, x, y + p, mode);
		}
	}
	
	private void paintBar(Graphics g, int x, int y) {
		int itemSpacing = g.getClipWidth() / 4;
		int speed = itemSpacing / STEPS;
		
		int height = Math.max(defaultFont.getHeight(), selectedFont.getHeight()) + PADDING * 2;
		g.setColor(0xFFFFFF);
		g.fillRect(0, y - height + PADDING, g.getClipWidth(), height);
		
		if(pidgeonShift != 0) {
			shiftAmount = pidgeonShift * itemSpacing;
			pidgeonShift = 0;
		}
		
		if(Math.abs(Math.abs(shiftAmount) - Math.abs(speed)) < speed) {
			shiftAmount = 0;
		}
		
		if(shiftAmount == 0) {
			g.setColor(INACTIVE_ITEM_COLOR,INACTIVE_ITEM_COLOR,INACTIVE_ITEM_COLOR);
			
			// Left one
			String leftChoice = choices.elementAt( wrap(choiceIndex - 1) ).toString();
			
			g.drawString(leftChoice, x - itemSpacing, y, Graphics.HCENTER | Graphics.BOTTOM);
			
			// Right one
			String rightChoice = choices.elementAt( wrap(choiceIndex + 1) ).toString();
			g.drawString(rightChoice, x + itemSpacing, y, Graphics.HCENTER | Graphics.BOTTOM);
			
			// Draw the current choice!
			g.setColor(ACTIVE_ITEM_COLOR,ACTIVE_ITEM_COLOR,ACTIVE_ITEM_COLOR);
			if(choices.size() > 0 && choiceIndex >= 0 && choiceIndex <= choices.size() - 1) {
				String currentChoice = choices.elementAt(choiceIndex).toString();
				g.drawString(currentChoice, x, y, Graphics.HCENTER | Graphics.BOTTOM);
			}
			
		}
		else {
			int dir = Math.abs(shiftAmount) / shiftAmount;
			
			String leftChoice = choices.elementAt( wrap(choiceIndex + dir - 1) ).toString();
			String rightChoice = choices.elementAt( wrap(choiceIndex + dir + 1) ).toString();
			String centerChoice = choices.elementAt( wrap(choiceIndex + dir) ).toString();
			String newChoice = choices.elementAt(  wrap(choiceIndex - dir)).toString();
			
			// Interpolate
			shiftAmount -= dir * speed;
			
			// Draw
			int mode = Graphics.HCENTER | Graphics.BOTTOM;
			int startColor, endColor, currentColor;
			
			// The Right item
			startColor = INACTIVE_ITEM_COLOR;
			endColor = (dir == -1) ? ACTIVE_ITEM_COLOR : BG_COLOR;
			currentColor = startColor + (endColor - startColor) * (itemSpacing - Math.abs(shiftAmount)) / itemSpacing;
			
			System.out.println("Start: " + startColor);
			System.out.println("End: " + endColor);
			System.out.println("Current: " + currentColor);
			
			g.setColor(currentColor,currentColor,currentColor);
			g.drawString(rightChoice, x + itemSpacing + dir * itemSpacing - shiftAmount, y, mode);
			
			// The left item
			endColor = (dir == 1) ? ACTIVE_ITEM_COLOR : BG_COLOR;
			currentColor = startColor + (endColor - startColor) * (itemSpacing - Math.abs(shiftAmount)) / itemSpacing;
			g.setColor(currentColor);
			g.setColor(currentColor,currentColor,currentColor);
			g.drawString(leftChoice, x - itemSpacing + dir * itemSpacing - shiftAmount, y, mode);
			
			// The center item
			startColor = ACTIVE_ITEM_COLOR;
			endColor = INACTIVE_ITEM_COLOR;
			currentColor = startColor + (endColor - startColor) * (itemSpacing - Math.abs(shiftAmount)) / itemSpacing;
			g.setColor(currentColor,currentColor,currentColor);
			g.drawString(centerChoice, x + dir * itemSpacing - shiftAmount, y, mode);
			
			// The new item
			startColor = BG_COLOR;
			endColor = INACTIVE_ITEM_COLOR;
			currentColor = startColor + (endColor - startColor) * (itemSpacing - Math.abs(shiftAmount)) / itemSpacing;
			g.setColor(currentColor,currentColor,currentColor);
			g.drawString(newChoice, x + -dir * itemSpacing - shiftAmount, y, mode);

			
		}
		
	}
	
	public void setChoice(int c) {
		if(c < 0 || c >= choices.size()) return;
		choiceIndex = c;
	}
	
	public void setRadius(int r) {
		diameter = 2 * r;
	}
	
	public void setDiameter(int d) {
		diameter = Math.max(minimumDiameter, d);
	}
	
	public int getRadius() {
		return diameter / 2;
	}
	
	public int getDiameter() {
		return diameter;
	}
	
	public int getHeight() {
		if (type == TYPE_CIRCULAR) 
			return diameter;
		else if(type == TYPE_LIST){
			if(defaultFont == null || selectedFont == null) return 0;
			return (choices.size() - 1) * defaultFont.getHeight() + selectedFont.getHeight();
		}
		else { // type == TYPE_BAR
			return Math.max(defaultFont.getHeight(), selectedFont.getHeight()) + PADDING * 2;
		}
	}
	
	public int getWidth() {
		if(type == TYPE_CIRCULAR)
			return diameter;
		else if(type == TYPE_LIST) {
			int max = 0;
			for(int i = 0; i < choices.size(); i++) {
				String s = choices.elementAt(i).toString();
				max = Math.max(selectedFont.charsWidth(s.toCharArray(), 0, s.length()), max);
			}
			return max;
		}
		else {
			return 0;
		}
	}
	
	
	public void nextChoice() {
		choiceIndex++;
		
		// Tell paint we need to shift
		if(type == TYPE_BAR) pidgeonShift--;
		
		if(choiceIndex < choices.size()) return;
		
		if(type == TYPE_CIRCULAR) choiceIndex = 0;
		else if(type == TYPE_LIST) choiceIndex = choices.size() - 1;
		else if(type == TYPE_BAR) {
			 choiceIndex = 0;
			 
		}
	}
	
	public void previousChoice() {
		choiceIndex--;
		
		// Tell paint() that we need to shift
		if(type == TYPE_BAR) pidgeonShift++;
		
		if(choiceIndex >= 0) return;
		
		if(type == TYPE_CIRCULAR || type == TYPE_BAR) choiceIndex = choices.size() - 1;
		else if(type == TYPE_LIST) choiceIndex = 0;
	}
	
	public int getChoiceIndex() {
		return choiceIndex;
	}
	
	public Object getChoiceObject() {
		return choices.elementAt(choiceIndex);
	}
	
	public void setDefaultFont(Font f) {
		defaultFont = f;
	}
	
	public void setSelectedFont(Font f) {
		selectedFont = f;
	}
	
	/**
	 * Wraps a given index back around the data structure
	 * @param i, the index
	 * @return the equivalent index
	 */
	private int wrap(int i) {
		if(choices.size() == 0) return 0;
		
		if(i >= choices.size()) return i % choices.size();
		else if(i < 0) {
			while(i < 0 || i >= choices.size()) {
				i += choices.size();
			}
			return i;
		}
		else return i;
	}
}
